#include "bsp_ili9341.h"

// 液晶屏扫描模式, 本变量主要用于方便选择触摸屏的计算参数
// 参数可选值为0~7
// 调用ili9341_gramscan函数设置方向时会自动更改
// LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE = 6;
uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;

static uint16_t CurrentTextColor   = BLACK;//前景色
static uint16_t CurrentBackColor   = WHITE;//背景色
/**
  * @brief  初始化ILI9341的IO引脚
  * @param  无
  * @retval 无
  */
static void bsp_ili9341_gpio_config (void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/* 使能FSMC对应相应管脚时钟 */
	RCC_APB2PeriphClockCmd ( 						/* 控制信号 */
													ILI9341_CS_CLK|ILI9341_DC_CLK|ILI9341_WR_CLK|
													ILI9341_RD_CLK	|ILI9341_BK_CLK|ILI9341_RST_CLK|
													/* 数据信号 */
													ILI9341_D0_CLK|ILI9341_D1_CLK|	ILI9341_D2_CLK | 
													ILI9341_D3_CLK | ILI9341_D4_CLK|ILI9341_D5_CLK|
													ILI9341_D6_CLK | ILI9341_D7_CLK|ILI9341_D8_CLK|
													ILI9341_D9_CLK | ILI9341_D10_CLK|ILI9341_D11_CLK|
													ILI9341_D12_CLK | ILI9341_D13_CLK|ILI9341_D14_CLK|
													ILI9341_D15_CLK	, ENABLE );
		
	
	/* 配置FSMC相对应的数据线,FSMC-D0~D15 */	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D0_PIN;
	GPIO_Init ( ILI9341_D0_PORT, & GPIO_InitStructure );

	GPIO_InitStructure.GPIO_Pin = ILI9341_D1_PIN;
	GPIO_Init ( ILI9341_D1_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D2_PIN;
	GPIO_Init ( ILI9341_D2_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D3_PIN;
	GPIO_Init ( ILI9341_D3_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D4_PIN;
	GPIO_Init ( ILI9341_D4_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D5_PIN;
	GPIO_Init ( ILI9341_D5_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D6_PIN;
	GPIO_Init ( ILI9341_D6_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D7_PIN;
	GPIO_Init ( ILI9341_D7_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D8_PIN;
	GPIO_Init ( ILI9341_D8_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D9_PIN;
	GPIO_Init ( ILI9341_D9_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D10_PIN;
	GPIO_Init ( ILI9341_D10_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D11_PIN;
	GPIO_Init ( ILI9341_D11_PORT, & GPIO_InitStructure );

	GPIO_InitStructure.GPIO_Pin = ILI9341_D12_PIN;
	GPIO_Init ( ILI9341_D12_PORT, & GPIO_InitStructure );	
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D13_PIN;
	GPIO_Init ( ILI9341_D13_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D14_PIN;
	GPIO_Init ( ILI9341_D14_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D15_PIN;
	GPIO_Init ( ILI9341_D15_PORT, & GPIO_InitStructure );
	

	
	/* 配置FSMC相对应的控制线
	 * FSMC_NOE   :LCD-RD
	 * FSMC_NWE   :LCD-WR
	 * FSMC_NE1   :LCD-CS
	 * FSMC_A16  	:LCD-DC
	 */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_RD_PIN; 
	GPIO_Init (ILI9341_RD_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_WR_PIN; 
	GPIO_Init (ILI9341_WR_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_CS_PIN; 
	GPIO_Init ( ILI9341_CS_PORT, & GPIO_InitStructure );  
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_DC_PIN; 
	GPIO_Init ( ILI9341_DC_PORT, & GPIO_InitStructure );
	

  /* 配置LCD复位RST控制管脚 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_RST_PIN; 
	GPIO_Init ( ILI9341_RST_PORT, & GPIO_InitStructure );
	
	
	/* 配置LCD背光控制管脚BK */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_BK_PIN; 
	GPIO_Init ( ILI9341_BK_PORT, & GPIO_InitStructure );
}

void bsp_ili9341_fsmc_config(void)
{
    FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 	

    /* 使能FSMC时钟 */
    RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_FSMC, ENABLE );

    // 地址建立时间(ADDSET) 为1个HCLK 2/72M = 28ns
    readWriteTiming.FSMC_AddressSetupTime      = 0x01;	 // 地址建立时间
    // 数据保持时间(DATAST) + 1个HCLK = 5/72M = 70ns	
    readWriteTiming.FSMC_DataSetupTime         = 0x04;	 // 数据建立时间
    // 选择控制的模式
    // 模式B,异步NOR FLASH模式, 与ILI8341的8080时序匹配
    readWriteTiming.FSMC_AccessMode            = FSMC_AccessMode_B;	

    /* 一下配置与模式B无关 */
    // 地址保持时间(ADDHLD) 模式A未用到
    readWriteTiming.FSMC_AddressHoldTime       = 0x00;	 // 地址保持时间
    // 设置总线转换周期, 仅用于服用模式的NOR操作
    readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
    // 设置时钟分频, 仅用于同步类型的存储器
    readWriteTiming.FSMC_CLKDivision           = 0x00;
    // 数据保持时间, 仅用于同步型的NOR
    readWriteTiming.FSMC_DataLatency           = 0x00;	


    FSMC_NORSRAMInitStructure.FSMC_Bank                  = FSMC_Bank1_NORSRAMx;
    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux        = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType            = FSMC_MemoryType_NOR;
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth       = FSMC_MemoryDataWidth_16b;
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode       = FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity    = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_WrapMode              = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive      = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStructure.FSMC_WriteOperation        = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignal            = FSMC_WaitSignal_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ExtendedMode          = FSMC_ExtendedMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WriteBurst            = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
    FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct     = &readWriteTiming;  

    FSMC_NORSRAMInit ( & FSMC_NORSRAMInitStructure ); 


    /* 使能 FSMC_Bank1_NORSRAM4 */
    FSMC_NORSRAMCmd ( FSMC_Bank1_NORSRAMx, ENABLE );  


}

void  bsp_ili9341_backled_control(FunctionalState enumState)
{
	if (enumState)
		GPIO_ResetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );	
	else
		GPIO_SetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );
}

void bsp_ili9341_rest(void)
{
	GPIO_ResetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );	 // 低电平复位

	delay_ms ( 0xAFF ); 					   

	GPIO_SetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );		 	 

	delay_ms ( 0xAFF ); 		
}

void bsp_ili9341_write_cmd (uint16_t usCmd)
{
	* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_CMD ) = usCmd;
	
}

void bsp_ili9341_write_data ( uint16_t usData )
{
	* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_DATA ) = usData;
	
}

/**
 * @brief  初始化ILI9341寄存器
 * @param  无
 * @retval 无
 */
static void bsp_ili9341_reg_config (void)
{
	bsp_ili9341_write_cmd(0xCF);
	/*  Power control B (CFh)  */
	 
	bsp_ili9341_write_cmd ( 0xCF  );
	bsp_ili9341_write_cmd ( 0x00  );
	bsp_ili9341_write_cmd ( 0x81  );
	bsp_ili9341_write_cmd ( 0x30  );
	
	/*  Power on sequence control (EDh) */
	   
	bsp_ili9341_write_cmd ( 0xED );
	bsp_ili9341_write_cmd ( 0x64 );
	bsp_ili9341_write_cmd ( 0x03 );
	bsp_ili9341_write_cmd ( 0x12 );
	bsp_ili9341_write_cmd ( 0x81 );
	
	/*  Driver timing control A (E8h) */
	   
	bsp_ili9341_write_cmd ( 0xE8 );
	bsp_ili9341_write_cmd ( 0x85 );
	bsp_ili9341_write_cmd ( 0x10 );
	bsp_ili9341_write_cmd ( 0x78 );
	
	/*  Power control A (CBh) */
	   
	bsp_ili9341_write_cmd ( 0xCB );
	bsp_ili9341_write_cmd ( 0x39 );
	bsp_ili9341_write_cmd ( 0x2C );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x34 );
	bsp_ili9341_write_cmd ( 0x02 );
	
	/* Pump ratio control (F7h) */
	   
	bsp_ili9341_write_cmd ( 0xF7 );
	bsp_ili9341_write_cmd ( 0x20 );
	
	/* Driver timing control B */
	   
	bsp_ili9341_write_cmd ( 0xEA );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x00 );
	
	/* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
	   
	bsp_ili9341_write_cmd ( 0xB1 );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x1B );
	
	/*  Display Function Control (B6h) */
	   
	bsp_ili9341_write_cmd ( 0xB6 );
	bsp_ili9341_write_cmd ( 0x0A );
	bsp_ili9341_write_cmd ( 0xA2 );
	
	/* Power Control 1 (C0h) */
	   
	bsp_ili9341_write_cmd ( 0xC0 );
	bsp_ili9341_write_cmd ( 0x35 );
	
	/* Power Control 2 (C1h) */
	   
	bsp_ili9341_write_cmd ( 0xC1 );
	bsp_ili9341_write_cmd ( 0x11 );
	
	/* VCOM Control 1 (C5h) */
	bsp_ili9341_write_cmd ( 0xC5 );
	bsp_ili9341_write_cmd ( 0x45 );
	bsp_ili9341_write_cmd ( 0x45 );
	
	/*  VCOM Control 2 (C7h)  */
	bsp_ili9341_write_cmd ( 0xC7 );
	bsp_ili9341_write_cmd ( 0xA2 );
	
	/* Enable 3G (F2h) */
	bsp_ili9341_write_cmd ( 0xF2 );
	bsp_ili9341_write_cmd ( 0x00 );
	
	/* Gamma Set (26h) */
	bsp_ili9341_write_cmd ( 0x26 );
	bsp_ili9341_write_cmd ( 0x01 );
	   
	
	/* Positive Gamma Correction */
	bsp_ili9341_write_cmd ( 0xE0 ); //Set Gamma
	bsp_ili9341_write_cmd ( 0x0F );
	bsp_ili9341_write_cmd ( 0x26 );
	bsp_ili9341_write_cmd ( 0x24 );
	bsp_ili9341_write_cmd ( 0x0B );
	bsp_ili9341_write_cmd ( 0x0E );
	bsp_ili9341_write_cmd ( 0x09 );
	bsp_ili9341_write_cmd ( 0x54 );
	bsp_ili9341_write_cmd ( 0xA8 );
	bsp_ili9341_write_cmd ( 0x46 );
	bsp_ili9341_write_cmd ( 0x0C );
	bsp_ili9341_write_cmd ( 0x17 );
	bsp_ili9341_write_cmd ( 0x09 );
	bsp_ili9341_write_cmd ( 0x0F );
	bsp_ili9341_write_cmd ( 0x07 );
	bsp_ili9341_write_cmd ( 0x00 );
	
	/* Negative Gamma Correction (E1h) */
	bsp_ili9341_write_cmd ( 0XE1 ); //Set Gamma
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x19 );
	bsp_ili9341_write_cmd ( 0x1B );
	bsp_ili9341_write_cmd ( 0x04 );
	bsp_ili9341_write_cmd ( 0x10 );
	bsp_ili9341_write_cmd ( 0x07 );
	bsp_ili9341_write_cmd ( 0x2A );
	bsp_ili9341_write_cmd ( 0x47 );
	bsp_ili9341_write_cmd ( 0x39 );
	bsp_ili9341_write_cmd ( 0x03 );
	bsp_ili9341_write_cmd ( 0x06 );
	bsp_ili9341_write_cmd ( 0x06 );
	bsp_ili9341_write_cmd ( 0x30 );
	bsp_ili9341_write_cmd ( 0x38 );
	bsp_ili9341_write_cmd ( 0x0F );
	
	/* memory access control set */
	   
	bsp_ili9341_write_cmd ( 0x36 ); 	
	bsp_ili9341_write_cmd ( 0xC8 );    /*ÊúÆÁ  ×óÉÏ½Çµ½ (Æðµã)µ½ÓÒÏÂ½Ç (ÖÕµã)É¨Ãè·½Ê½*/
	   
	
	/* column address control set */
	bsp_ili9341_write_cmd ( CMD_SetCoordinateX ); 
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0xEF );
	
	/* page address control set */
	   
	bsp_ili9341_write_cmd ( CMD_SetCoordinateY ); 
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x00 );
	bsp_ili9341_write_cmd ( 0x01 );
	bsp_ili9341_write_cmd ( 0x3F );
	
	/*  Pixel Format Set (3Ah)  */
	   
	bsp_ili9341_write_cmd ( 0x3a ); 
	bsp_ili9341_write_cmd ( 0x55 );
	
	/* Sleep Out (11h)  */
	bsp_ili9341_write_cmd ( 0x11 );	
	delay_ms ( 0xAFFf<<2 );
	   
	
	/* Display ON (29h) */
	bsp_ili9341_write_cmd ( 0x29 ); 

}

/*
A表示向上 V表示向下 <表示向左 >表示向右
X表示X轴 Y表示Y轴
------------------------------------------------------------
模式0				.		模式1		.	模式2			.	模式3					
					A		.					A		.		A					.		A									
					|		.					|		.		|					.		|							
					Y		.					X		.		Y					.		X					
					0		.					1		.		2					.		3					
	<--- X0 o		.	<----Y1	o		.		o 2X--->  .		o 3Y--->	
------------------------------------------------------------	
模式4				.	模式5			.	模式6			.	模式7					
	<--- X4 o		.	<--- Y5 o		.		o 6X--->  .		o 7Y--->	
					4		.					5		.		6					.		7	
					Y		.					X		.		Y					.		X						
					|		.					|		.		|					.		|							
					V		.					V		.		V					.		V		
---------------------------------------------------------	
*/			
void bsp_ili9341_gram_scan(uint8_t ucOption)
{
	//参数检查, 只可输入0~7
	if(ucOption >7 )
		return;

	//更具模式更新LCD_SCAN_MODE的值, 主要用于触摸屏选择计算参数
	LCD_SCAN_MODE = ucOption;

	//根据模式更新XY方向的像素宽度
	if(ucOption%2 == 0)	
	{
		//0 2 4 6模式下X方向像素宽度为240, Y方向为320
		LCD_X_LENGTH = ILI9341_LESS_PIXEL;
		LCD_Y_LENGTH =	ILI9341_MORE_PIXEL;
	}
	else				
	{
		//1 3 5 7模式下X方向像素宽度为320, Y方向为240
		LCD_X_LENGTH = ILI9341_MORE_PIXEL;
		LCD_Y_LENGTH =	ILI9341_LESS_PIXEL; 
	}

	//0x36命令参数的高3位可用于设置GRAM
	bsp_ili9341_write_cmd ( 0x36 ); 
	bsp_ili9341_write_cmd ( 0x08 |(ucOption<<5));//¸ù¾ÝucOptionµÄÖµÉèÖÃLCD²ÎÊý£¬¹²0-7ÖÖÄ£Ê½
	bsp_ili9341_write_cmd ( CMD_SetCoordinateX ); 
	bsp_ili9341_write_cmd ( 0x00 );		/* x ÆðÊ¼×ø±ê¸ß8Î» */
	bsp_ili9341_write_cmd ( 0x00 );		/* x ÆðÊ¼×ø±êµÍ8Î» */
	bsp_ili9341_write_cmd ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x ½áÊø×ø±ê¸ß8Î» */	
	bsp_ili9341_write_cmd ( (LCD_X_LENGTH-1)&0xFF );				/* x ½áÊø×ø±êµÍ8Î» */

	bsp_ili9341_write_cmd ( CMD_SetCoordinateY ); 
	bsp_ili9341_write_cmd ( 0x00 );		/* y ÆðÊ¼×ø±ê¸ß8Î» */
	bsp_ili9341_write_cmd ( 0x00 );		/* y ÆðÊ¼×ø±êµÍ8Î» */
	bsp_ili9341_write_cmd ( ((LCD_Y_LENGTH-1)>>8)&0xFF );	/* y ½áÊø×ø±ê¸ß8Î» */	 
	bsp_ili9341_write_cmd ( (LCD_Y_LENGTH-1)&0xFF );				/* y ½áÊø×ø±êµÍ8Î» */

	/* write gram start */
	bsp_ili9341_write_cmd ( CMD_SetPixel );	
}

void  bsp_ili9341_init()
{

    bsp_ili9341_gpio_config();
    bsp_ili9341_fsmc_config();
	bsp_ili9341_backled_control(ENABLE);
	bsp_ili9341_rest();
	bsp_ili9341_reg_config();
	// 设置默认扫描方向,其中模式 6 为大部分液晶历程的默认显示方向
	bsp_ili9341_gram_scan(LCD_SCAN_MODE);
}

/**
 * @brief  在ILI9341显示器上开辟一个窗口
 * @param  usX 在特定扫描方向下窗口的起点X坐标
 * @param  usY 在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth 窗口的宽度
 * @param  usHeight 窗口的高度
 * @retval 无
 */
static void bsp_ili9341_open_window ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{	
	bsp_ili9341_write_cmd ( CMD_SetCoordinateX ); 				 /* 设置X坐标 */
	bsp_ili9341_write_data ( usX >> 8  );	 /*先高8位 然后低8位 */
	bsp_ili9341_write_data ( usX & 0xff  );	 /* 设置起始点和结束点 */
	bsp_ili9341_write_data ( ( usX + usWidth - 1 ) >> 8  );
	bsp_ili9341_write_data ( ( usX + usWidth - 1 ) & 0xff  );

	bsp_ili9341_write_cmd ( CMD_SetCoordinateY ); 			     /* 设置Y坐标 */
	bsp_ili9341_write_data ( usY >> 8  );
	bsp_ili9341_write_data ( usY & 0xff  );
	bsp_ili9341_write_data ( ( usY + usHeight - 1 ) >> 8 );
	bsp_ili9341_write_data ( ( usY + usHeight - 1) & 0xff );
	
}

static __inline void bsp_ili9341_fill_color ( uint32_t ulAmout_Point, uint16_t usColor )
{
	uint32_t i = 0;
	
	
	/* memory write */
	bsp_ili9341_write_cmd ( CMD_SetPixel );	
		
	for ( i = 0; i < ulAmout_Point; i ++ )
		bsp_ili9341_write_data ( usColor );
		
	
}


/**
 * @brief  对ILI9341显示器的某一窗口以某种颜色进行清屏
 * @param  usX 在特定的扫描方向下窗口的起点X坐标
 * @param  usY 在特定的扫描方向下窗口的起点Y坐标
 * @param  usWidth 窗口的宽度
 * @param  usHeight 窗口的高度
 * @note 可使用LCD_SetBackColor LCD_SetTextColor LCD_SetColors函数设置颜色
 * @retval 无
 */
void bsp_ili9341_clear( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{
	bsp_ili9341_open_window ( usX, usY, usWidth, usHeight );
	bsp_ili9341_fill_color ( usWidth * usHeight, CurrentBackColor );		
	
}


